#pragma once

#include <iostream>
#include <array>
#include <vector>

namespace ProgramWhileComplie
{
    /**
     * 8、编译时编程
     * 8.1.模板元编程
     * 8.2.使用constexpr进行计算
     * 8.3.使用偏特化的执行路径选择
     * 8.4.SFINAE(替换失败不为过)
     * 8.5.编译时if
     */

    /**
     * 8.2.使用constexpr进行计算：编译期计算值
     */
    constexpr bool isOddNum(int num)
    {
        return num % 2;
    }

    /**
     * 8.3.使用偏特化进行路径选择
     * 因为函数模板不支持偏特化，所以必须使用其他机制根据某些约束来更改函数实现：
     * • 带有静态函数的类，
     * • std::enable_if，在第 6.3 节中介绍。
     * • SFINAE 特性，
     * • 编译时 if 特性，该特性从 C++17 引入，将在第 8.5 节中介绍。
     */

    // OddHelper 根据第二个参数进行路径选择
    template <std::size_t SZ, bool = isOddNum(SZ)>
    struct OddHelper
    {
    };

    // SZ为奇数
    template <std::size_t SZ>
    struct OddHelper<SZ, true>
    {
        static constexpr bool value = true;
    };

    // SZ为偶数
    template <std::size_t SZ>
    struct OddHelper<SZ, false>
    {
        static constexpr bool value = false;
    };

    template <typename T, std::size_t SZ>
    bool foo(const std::array<T, SZ> &arr)
    {
        return OddHelper<SZ>::value; // 静态变量，不需要使用 typename 修饰！！！
    }

    /**
     * 4、SFINAE(Substitution failure is not an error,形参失败不是一个错误)，这玩意好像比较复杂，后面还会看，先简单写点实例代码
     * 4.1.SFINAE 和重载解析
     * 4.2.SFINAE 与 decltype
     */
    /*
    4.2.SFINAE与decltype
     */

    // 对于数组会实例化该模板
    template <typename T, std::size_t N>
    size_t len(T (&)[N])
    {
        return N;
    }

    // 对于vector、array等包含size()成员函数的类会实例化以下模板
    // 使用 auto...->decltype 的意义是：保证T存在函数size()，且返回的类型为 T::size_type
    // 将表达式强制转换为 void，是为了避免表达式使用了用户重载定义的逗号操作符。
    template <typename T>
    auto len(const T &t) -> decltype((void)(t.size()), typename T::size_type())
    {
        return t.size();
    }

    // 对于所有不匹配的类型，len函数最终会返回0
    // 实践中，保底候选函数通常会提供更有用的默认值、抛出异常或包含静态断言输出更有用的错误消息。
    std::size_t len(...)
    {
        return 0;
    }

    /**
     * 5、编译时if (since C++17)
     *  C++17 引入了编译时 if 语句，允许根据编译时条件启用或禁用特定语句。
     *  使用 if constexpr(…) 语法，编译器使用编译时表达式来决定是应用 then 部分，还是 else 部分 (如果有的话)。
     */

    /*
    // variadicparamtemplate.hpp 提到的问题：
    template <typename T, typename... Types>
    void print(const T &firstArg, const Types &...args)
    {
        std::cout << firstArg << std::endl;
        if(sizeof...(args) > 0) // 不会在编译期进行 sizeof...(args)==0 的情况下，也会进行实例化，导致print出错
        {
            print(args...);
        }
    }
    */

    template <typename T, typename... Types>
    void print(const T &firstArg, const Types &...args)
    {
        std::cout << firstArg << " ";
        if constexpr (sizeof...(args) > 0) // 编译期如果 sizeof...(args)>0 就不进行print的实例化，即编译期就进行检查不满足if就不会实例化模板(即被丢弃)
        {
            print(args...);
        }
        else
        {
            // static_assert(false,"aaa"); // error,false编译期就被触发
            static_assert(std::is_integral_v<T>, "no integral");
            std::cout << std::endl;
        }
    }

};